<!DOCTYPE html>
<html>

<head>
    <!--<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/eruda"></script>

    <script>
        eruda.init();
    </script>-->
    <!-- 百度统计 -->
    <script>
        var _hmt = _hmt || [];
        (function() {
            var hm = document.createElement("script");
            hm.src = "https://hm.baidu.com/hm.js?f61b0a58a7ac2562d1228f6d33523b60";
            var s = document.getElementsByTagName("script")[0]; 
            s.parentNode.insertBefore(hm, s);
        })();
    </script>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="keywords" content="Nolos五子棋" />
    <meta name="description" content="一个简易的在线五子棋软件。来看看你能不能打败它！" />
    <title>Nolos五子棋[专业模式]</title>
    <style>
        .help {
            background-color: #4caf50;
            border: 2px solid #4caf50;
            border-radius: 7px;
            color: white;
            padding: 5px 10px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
        }

        .help:hover {
            transition-duration: 500ms;
            background-color: #ffffff;
            color: #4caf50;
        }

        .help:active {
            transition-duration: 0ms;
            background-color: darkgreen;
            color: white;
        }

        .help:disabled {
            background-color: #4caf50;
            color: white;
            opacity: .6;
            cursor: not-allowed;
        }

        .help_used {
            background-color: #03c3d1;
            border: 2px solid #03c3d1;
            border-radius: 7px;
            color: white;
            padding: 5px 10px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
        }

        .help_used:active {
            transition-duration: 0ms;
            background-color: #0081cc;
            color: white;
        }

        .help_used:hover {
            transition-duration: 500ms;
            background-color: #ffffff;
            color: #03c3d1;
        }

        .help_used:disabled {
            background-color: #03c3d1;
            color: white;
            opacity: .6;
            cursor: not-allowed;
        }
    </style>
    <script>
        var black = 2;
        var white = 1;
        var empty = 0;
        var human = white;
        var nolos = black;
        const INFINITY = 999999, threshold = 19000;
        var step = 1, over = false, qp = [], wins = [], bwin = [], wwin = [], iswin = {}, wincount = 0, board, context, laststep, d1, d2;
        var canVCT = (value) => value >= 271;
        for (var i = 0; i < 15; i++) {
            qp[i] = [];
            wins[i] = [];
            for (var j = 0; j < 15; j++) {
                qp[i][j] = empty;
                wins[i][j] = [];
            }
        }
        for (var i = 0; i < 15; i++) {
            for (var j = 0; j < 11; j++) {
                for (var k = 0; k < 5; k++) {
                    wins[i][j + k][wincount] = true;
                }
                wincount++;
            }
        }
        for (var i = 0; i < 15; i++) {
            for (var j = 0; j < 11; j++) {
                for (var k = 0; k < 5; k++) {
                    wins[j + k][i][wincount] = true;
                }
                wincount++;
            }
        }
        for (var i = 0; i < 11; i++) {
            for (var j = 0; j < 11; j++) {
                for (var k = 0; k < 5; k++) {
                    wins[i + k][j + k][wincount] = true;
                }
                wincount++;
            }
        }
        for (var i = 0; i < 11; i++) {
            for (var j = 14; j > 3; j--) {
                for (var k = 0; k < 5; k++) {
                    wins[i + k][j - k][wincount] = true;
                }
                wincount++;
            }
        }
        for (var i = 0; i < wincount; i++) {
            bwin[i] = 0;
            wwin[i] = 0;
        }
        paintqp();

        var reverse = function (player) {
            return player == black ? white : black;
        }

        function paintqp() {
            document.write("<center><span id='timer'><font size=20>Nolos五子棋</font></span><br><canvas id=\"cboard\" width=540 height=540></canvas><br><button onclick='forward();' class=help id=help><font size=4>提示一步</font><br>（这会交换黑白双方！）</button></center>");
            board = document.getElementById("cboard");
            context = board.getContext("2d");
            var img = new Image();
            img.src = "./background.bmp";
            img.onload = () => {
                context.drawImage(img, 0, 0, 540, 540);
                context.fillStyle = "black";
                for (var i = 0; i < 15; i++) {
                    context.moveTo(18 + i * 36, 18);
                    context.lineTo(18 + i * 36, 522);
                    context.stroke();
                    context.moveTo(18, 18 + i * 36);
                    context.lineTo(522, 18 + i * 36);
                    context.stroke();
                }
                context.beginPath();
                context.arc(18 + 3 * 36, 18 + 3 * 36, 4, 0, 2 * Math.PI);
                context.fill();
                context.beginPath();
                context.arc(18 + 11 * 36, 18 + 11 * 36, 4, 0, 2 * Math.PI);
                context.fill();
                context.beginPath();
                context.arc(18 + 3 * 36, 18 + 11 * 36, 4, 0, 2 * Math.PI);
                context.fill();
                context.beginPath();
                context.arc(18 + 11 * 36, 18 + 3 * 36, 4, 0, 2 * Math.PI);
                context.fill();
                context.beginPath();
                context.arc(18 + 7 * 36, 18 + 7 * 36, 4, 0, 2 * Math.PI);
                context.fill();
                board.onclick = function (e) {
                    if (over) return;
                    var i = Math.floor(e.offsetX / 36);
                    var j = Math.floor(e.offsetY / 36);
                    act(i, j);
                };
                setv(7, 7, black);
            }
        }

        function getv(i, j) {
            return qp[i][j];
        }

        function setv(i, j, chess, notdraw) {
            qp[i][j] = chess;
            for (var k = 0; k < wincount; k++) {
                if (wins[i][j][k]) {
                    if (chess == black) {
                        bwin[k]++;
                        wwin[k] = 6;
                        iswin[black] = (bwin[k] == 5 | iswin[black]);
                    } else {
                        wwin[k]++;
                        bwin[k] = 6;
                        iswin[white] = (wwin[k] == 5 | iswin[white]);
                    }
                }
            }
            if (notdraw) return;
            context.beginPath();
            context.arc(18 + i * 36, 18 + j * 36, 16, 0, 2 * Math.PI);
            context.fillStyle = chess == black ? "black" : "white";
            context.fill();
            context.beginPath();
            context.arc(18 + i * 36, 18 + j * 36, 7, 0, 2 * Math.PI);
            context.fillStyle = "red";
            context.fill();
            context.font = "16px bold calibri";
            context.textAlign = "center";
            context.textBaseline = "middle";
            if (laststep) {
                if (chess == black) {
                    context.beginPath();
                    context.arc(18 + laststep[0] * 36, 18 + laststep[1] * 36, 8, 0, 2 * Math.PI);
                    context.fillStyle = "white";
                    context.fill();
                    context.fillStyle = "black";
                    context.fillText(step, 18 + laststep[0] * 36, 18 + laststep[1] * 36);
                } else {
                    context.beginPath();
                    context.arc(18 + laststep[0] * 36, 18 + laststep[1] * 36, 8, 0, 2 * Math.PI);
                    context.fillStyle = "black";
                    context.fill();
                    context.fillStyle = "white";
                    context.fillText(step, 18 + laststep[0] * 36, 18 + laststep[1] * 36);
                }
            }
            laststep = [i, j];
        }

        function act(x, y) {
            if (getv(x, y) == empty) {
                d1 = (new Date()).getTime();
                setv(x, y, human);
                if (iswin[human] == true) {
                    document.getElementById("timer").innerHTML = human == white ? "白棋胜！请重新启动程序。" : "黑棋胜！请重新启动程序。";
                    over = true;
                    document.getElementById("help").disabled = over;
                    return;
                }
                step++;
                if (step >= 225) {
                    document.getElementById("timer").innerHTML = "平局！请重新启动程序。";
                    over = true;
                    document.getElementById("help").disabled = over;
                    return;
                }
                over = true;
                document.getElementById("help").disabled = over;
                document.getElementById("timer").innerHTML = "电脑正在思考……";
                setTimeout(function () {
                    ai();
                    step++;
                    if (iswin[nolos] == true) {
                        document.getElementById("timer").innerHTML = nolos == white ? "白棋胜！请重新启动程序。" : "黑棋胜！请重新启动程序。";
                        return;
                    }
                    if (step >= 225) {
                        document.getElementById("timer").innerHTML = "平局！请重新启动程序。";
                        return;
                    }
                    over = false;
                    document.getElementById("help").disabled = over;
                    d2 = (new Date()).getTime();
                    document.getElementById("timer").innerHTML = "本次电脑思考用时" + (d2 - d1 - 100).toString() + "毫秒。";
                }, 100);
            }
        }

        function forward() {
            if (step != 1) {
                alert("非开发人员只能在第一步使用！");
                return;
            }
            document.getElementById("help").className = "help_used";
            human = reverse(human);
            nolos = reverse(nolos);
            over = true;
            document.getElementById("help").disabled = over;
            document.getElementById("timer").innerHTML = "电脑正在思考……";
            d1 = (new Date()).getTime();
            setTimeout(function () {
                ai();
                step++;
                if (iswin[nolos] == true) {
                    document.getElementById("timer").innerHTML = nolos == white ? "白棋胜！请重新启动程序。" : "黑棋胜！请重新启动程序。";
                    return;
                }
                if (step >= 225) {
                    document.getElementById("timer").innerHTML = "平局！请重新启动程序。";
                    return;
                }
                over = false;
                document.getElementById("help").disabled = over;
                d2 = (new Date()).getTime();
                document.getElementById("timer").innerHTML = "本次电脑思考用时" + (d2 - d1 - 100).toString() + "毫秒。";
            }, 100);
        }

        function ai() {
            var bests = [];
            for (var i = 0; i < 15; i++) {
                for (var j = 0; j < 15; j++) {
                    if (getv(i, j) == empty) {
                        bests.push([i, j, eval(i, j, nolos) * 1 + eval(i, j, human)]);
                    }
                }
            }
            bests.sort((a, b) => b[2] - a[2]);
            bests.splice(7);
            for (var i = 0; i < bests.length; i++) {
                var _bwin = [], _wwin = [];
                for (var j = 0; j < wincount; j++) {
                    _bwin[j] = bwin[j];
                    _wwin[j] = wwin[j];
                }
                setv(bests[i][0], bests[i][1], nolos, true);
                if (iswin[nolos]) {
                    bests[i][2] = INFINITY;
                    qp[bests[i][0]][bests[i][1]] = empty;
                    for (var j = 0; j < wincount; j++) {
                        bwin[j] = _bwin[j];
                        wwin[j] = _wwin[j];
                    }
                    iswin = { black: false, white: false };
                    break;
                }
                bests[i][2] = -search(human, Math.min(5, step + 1), -INFINITY, INFINITY);
                qp[bests[i][0]][bests[i][1]] = empty;
                for (var j = 0; j < wincount; j++) {
                    bwin[j] = _bwin[j];
                    wwin[j] = _wwin[j];
                }
                console.log("落在" + bests[i][0] + "，" + bests[i][1] + "处的评分是" + bests[i][2] + "分。");
                if (bests[i][2] >= INFINITY) break;
            }
            bests.sort((a, b) => b[2] - a[2]);
            setv(bests[0][0], bests[0][1], nolos);
            console.log("电脑选择落在" + bests[0][0] + "，" + bests[0][1] + "处，" + (bests[0][2] >= INFINITY ? "计算机必胜！" : bests[0][2] <= -INFINITY ? "玩家必胜！" : "未见分晓！"));
        }
        //打分函数
        function eval(x, y, player) {
            var score = 0;
            for (var k = 0; k < wincount; k++) {
                if (wins[x][y][k]) {
                    switch (player == black ? bwin[k] : wwin[k]) {
                        case 1:
                            score += 1;
                            break;
                        case 2:
                            score += 40;
                            break;
                        case 3:
                            score += 120;
                            break;
                        case 4:
                            score += 20000;
                            break;
                    }
                }
            }
            return score;
        }

        function evalglob(player) {
            var score = 0;
            for (var k = 0; k < wincount; k++) {
                switch (player == black ? bwin[k] : wwin[k]) {
                    case 1:
                        score += 1;
                        break;
                    case 2:
                        score += 40;
                        break;
                    case 3:
                        score += 120;
                        break;
                    case 4:
                        score += 20000;
                        break;
                }
            }
            return score;
        }

        function search(player, depth, alpha, beta) {
            if (depth == 0) return evalglob(player) - evalglob(reverse(player));
            var bests = [], self_score = 0, enemy_score = 0;
            for (var i = 0; i < 15; i++) {
                for (var j = 0; j < 15; j++) {
                    if (getv(i, j) == empty) {
                        self_score = eval(i, j, player);
                        enemy_score = eval(i, j, reverse(player));
                        bests.push([i, j, self_score + enemy_score, canVCT(self_score) || canVCT(enemy_score)]);
                    }
                }
            }
            if (bests.length == 0) return evalglob(player) - evalglob(reverse(player));
            bests.sort((a, b) => b[2] - a[2]);
            if (depth == 4 || depth == 5 && step >= 6 && bests[0][3]) {
                var ret_val = VCT(player, 5 + depth, false);
                if (ret_val >= INFINITY || ret_val <= -INFINITY) {
                    return ret_val;
                }
            }
            bests.splice(7);
            var value;
            var _bwin = [], _wwin = [];
            for (var i = 0; i < bests.length; i++) {
                for (var j = 0; j < wincount; j++) {
                    _bwin[j] = bwin[j];
                    _wwin[j] = wwin[j];
                }
                setv(bests[i][0], bests[i][1], player, true);
                if (iswin[player]) {
                    qp[bests[i][0]][bests[i][1]] = empty;
                    for (var j = 0; j < wincount; j++) {
                        bwin[j] = _bwin[j];
                        wwin[j] = _wwin[j];
                    }
                    iswin = { black: false, white: false };
                    return INFINITY;
                }
                value = -search(reverse(player), depth - 1, -beta, -alpha);
                qp[bests[i][0]][bests[i][1]] = empty;
                for (var j = 0; j < wincount; j++) {
                    bwin[j] = _bwin[j];
                    wwin[j] = _wwin[j];
                }
                if (value > alpha) alpha = value;
                if (alpha >= beta) break;
            }
            return alpha;
        }

        function VCT(player, maxdepth, parentCanVCT) {
            if (maxdepth == 0) return 0;
            var bests = [], self_score = 0, enemy_score = 0, v = -INFINITY;
            for (var i = 0; i < 15; i++) {
                for (var j = 0; j < 15; j++) {
                    if (getv(i, j) == empty) {
                        self_score = eval(i, j, player);
                        enemy_score = eval(i, j, reverse(player));
                        bests.push([i, j, self_score + enemy_score, canVCT(self_score) || canVCT(enemy_score)]);
                    }
                }
            }
            bests.sort((a, b) => b[2] - a[2]);
            if (!bests[0][3] && !parentCanVCT) return 0;
            bests.splice(3);
            var _bwin = [], _wwin = [];
            for (var i = 0; i < bests.length; i++) {
                for (var j = 0; j < wincount; j++) {
                    _bwin[j] = bwin[j];
                    _wwin[j] = wwin[j];
                }
                setv(bests[i][0], bests[i][1], player, true);
                if (iswin[player]) {
                    qp[bests[i][0]][bests[i][1]] = empty;
                    for (var j = 0; j < wincount; j++) {
                        bwin[j] = _bwin[j];
                        wwin[j] = _wwin[j];
                    }
                    iswin = { black: false, white: false };
                    return INFINITY;
                }
                v = Math.max(v, -VCT(reverse(player), maxdepth - 1, bests[i][3]));
                qp[bests[i][0]][bests[i][1]] = empty;
                for (var j = 0; j < wincount; j++) {
                    bwin[j] = _bwin[j];
                    wwin[j] = _wwin[j];
                }
                if (v >= INFINITY) break;
            }
            return v;
        }
    </script>
</head>

</html>